<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        //【1】构造函数模式和工厂模式的区别；
        // 创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型；而这正是构造函数模式胜过工厂模式的地方。比如下面例子，就成功归纳出了Person这样一个类型
        // 1.没有显式地创建对象；
        // 2.直接将属性和方法赋给了 this 对象；
        // 3.没有 return 语句
        function Person(name, age, job) {
            this.name = name;
            this.age = age;
            this.job = job;
            this.sayName = function() {
                alert(this.name);
            };
        }
        //【2】new操作符会经历下面四个步骤
        // (1) 创建一个新对象；
        // (2) 将构造函数的作用域赋给新对象（因此 this 就指向了这个新对象）；
        // (3) 执行构造函数中的代码（为这个新对象添加属性）；
        // (4) 返回新对象
        var person1 = new Person("Nicholas", 29, "Software Engineer");
        var person2 = new Person("Greg", 27, "Doctor");
        //【3】每个实例对象都有一个constructor属性，该属性都指向构造函数。
        alert(person1.constructor == Person); //true 
        alert(person2.constructor == Person); //true
        //【4】数据类型检测问题[提到检测对象类型，还是 instanceof 操作符要更可靠一些]
        alert(person1 instanceof Object); //true 
        alert(person1 instanceof Person); //true 
        alert(person2 instanceof Object); //true 
        alert(person2 instanceof Person); //true
        //【5-1】将构造函数当作函数
        // 当作构造函数使用
        var person = new Person("Nicholas", 29, "Software Engineer");
        person.sayName(); //"Nicholas" 
        // 作为普通函数调用
        Person("Greg", 27, "Doctor"); // 添加到 window 【当在全局作用域中调用一个函数时，this 对象总是指向 Global 对象】
        window.sayName(); //"Greg" 
        // 在另一个对象的作用域中调用
        var o = new Object();
        Person.call(o, "Kristen", 25, "Nurse");
        o.sayName(); //"Kristen"
        //【5-2】构造函数的问题
        // 每个方法都要在每个实例上重新创建一遍。在前面的例子中，person1 和 person2 都有一个名为 sayName()的方法，
        // 但那两个方法不是同一个 Function 的实例，实例化了两个不同的对象，虽然内部代码是一样的。
        function Person(name, age, job) {
            this.name = name;
            this.age = age;
            this.job = job;
            this.sayName = new Function("alert(this.name)"); // 与声明函数在逻辑上是等价的
        }
        alert(person1.sayName == person2.sayName); //false 
        //一种解决的思路【在全局作用域中定义的函数实际上只能被某个对象调用，这让全局作用域有点名不副实。而更让人无法接受的是：
        // 如果对象需要定义很多方法，那么就要定义很多个全局函数，于是我们这个自定义的引用类型就丝毫没有封装性可言了】
        function Person(name, age, job) {
            this.name = name;
            this.age = age;
            this.job = job;
            this.sayName = sayName;
        }

        function sayName() {
            alert(this.name);
        }
        var person1 = new Person("Nicholas", 29, "Software Engineer");
        var person2 = new Person("Greg", 27, "Doctor");
    </script>
</body>

</html>